#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <sys/epoll.h>
#include "Socket.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"

using namespace socket_ns;
using namespace log_ns;

class EpollServer {
private:
    enum {
        E_CREATE_ERR = 1,
        E_CTL_ERR,
        E_WAIT_ERR,
        RECV_ERR
    };

    static const int ev_num = 128;
    static const int ev_size = 128;
    static const int buff_size = 4096;

    void HandlerListenEvents() {
        // 现在 listen socket 开始接收消息
        InetAdrr addr;
        int sockfd = _listensock->CreateAccepte(&addr);
        if (sockfd < 0) {
            LOG(ERROR, "accepte the sock fail\n");
            return;
        }
        LOG(INFO, "recive a new socket: %d, client info: %s:%d\n", sockfd, addr.Ip().c_str(), addr.Port());
        // 现在将 sockfd 添加到 epfd 中
        struct epoll_event ev;
        ev.data.fd = sockfd;
        ev.events = EPOLLIN;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
        if (n < 0) {
            LOG(FATAL, "epoll ctl error\n");
            exit(E_CTL_ERR);
        }
        LOG(INFO, "epoll add sockfd successfully, fd: %d\n", sockfd);        
    }

    void HandlerOtherEvents(int fd) {
        // 现在可以开始读数据了
        char buff[buff_size];
        int n = recv(fd, buff, sizeof(buff) - 1, 0);
        if (n > 0) {
            buff[n] = 0;
            std::cout << buff << std::endl;
            std::string content = "<html><body><h1>hello Linux!</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;
            // echo_str += buffer;
            send(fd, echo_str.c_str(), echo_str.size(), 0); // 临时方案
        } else if (n == 0) {
            LOG(INFO, "client quit... to close fd: %d\n", fd);
            // 将fd从数组中去掉
            epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            close(fd);
        } else {
            LOG(FATAL, "recv error\n");
            epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            close(fd);
            exit(RECV_ERR);
        }
    }

    std::string EventStr(uint32_t rev) {
        if (rev == EPOLLOUT) return "EPOLLOUT";
        else if (rev == EPOLLIN) return "EPOLLIN";
        else return "EPOLLIN | EPOLLOUT";
    }

    void HandlerEvents(int num) {
        for (int i = 0; i < num; i++) {
            int fd = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;
            LOG(INFO, "%d socket are ready, the thing is %s\n", fd, EventStr(revents).c_str());
            if (fd == _listensock->GetSockfd())
                HandlerListenEvents();
            else
                HandlerOtherEvents(fd);
        }
    }

public:
    EpollServer(int port)
        : _listensock(std::make_unique<TcpSocket>(port))
    {
        _listensock->BuildListenSocket(port);
        _epfd = epoll_create(ev_size);
        if (_epfd < 0) {
            LOG(FATAL, "epoll create error\n");
            exit(E_CREATE_ERR);
        }
        LOG(INFO, "epoll create success, fd: %d\n", _epfd);
    }

    void Init() {
        // 现在将listensock加入到等待事件中
        struct epoll_event ev;
        ev.data.fd = _listensock->GetSockfd();
        ev.events = EPOLLIN;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->GetSockfd(), &ev);
        if (n < 0) {
            LOG(FATAL, "epoll ctl error\n");
            exit(E_CTL_ERR);
        }
        LOG(INFO, "epoll add listenfd successfully, fd: %d\n", _listensock->GetSockfd());
    }

    void Start() {
        // 设置超时是时间 1000ms
        int timeout = 1000;
        while (true) {
            int n = epoll_wait(_epfd, _revs, ev_num, timeout);
            switch (n) {
            case 0:
                LOG(INFO, "time out...\n");
                break;
            case -1:
                LOG(FATAL, "epoll wait error\n");
                exit(E_WAIT_ERR);
                break;
            default:
                LOG(INFO, "have some descriptor become ready\n");
                HandlerEvents(n);
                break;
            }
        }
    }

    ~EpollServer() {
        if (_epfd >= 0) close(_epfd);
        _listensock->Close();
    }

private:
    std::unique_ptr<Socket> _listensock;
    int _epfd;
    struct epoll_event _revs[ev_num];
};